Release 10.1A: OpenEdge Development:
Progress Dynamics Advanced Development
Defining the manager itself
In the AppBuilder, select New
Structured Manager to create the new manager procedure from its template. Enter the standard file documentation information in the wizard. Name the procedure fldedsrvrp.p. Note that this server-side manager procedure in fact contains all the code for both the client and server versions of the manager. As you write code for the manager, the
DB-REQUIREDflag determines which code gets compiled into which version of the manager.To create the client-side manager procedure, simply copy the client manager template, which is the file
af/sup2/aftemclntp.p, to the working directory where you want your manager. Name your version fldedclntp.p. Edit the file to include the server manager procedure fldedsrvrp.p, including its relative pathname, depending on your own directory structure, as shown:
As you can see, the client procedure simply defines the
DB-REQUIREDflag to be false and then includes the principal (server) manager procedure. In this way, the references toDB-REQUIREDinside fldedsrvrp.pdetermine which code gets compiled out of the client procedure. Save the client manager procedure and compile it when you have finished writing the manager. All your code now goes into fldedsrvrp.p.Data caching considerations
The first procedure to define in the manager, cacheFieldEdits, builds a client-side cache of data from the
Entity Field Edittable.Different kinds of managers might need to cache different data on either the client or server or both. Keep the following things in mind when you’re designing the caching mechanism for a manager:
- If code that is executed on the server needs quick access to data, it might be worthwhile to cache that data in temp-tables on the server side. This is especially true if the data must be massaged in some way as it is read out of whatever underlying database tables or other data sources provide the raw information, and if that data processing is relatively expensive. Caching the data once it has been read and processed makes it available for later access without incurring that overhead again.
- If there is a benefit to avoiding the caching overhead when a request comes in from a client, then the server start-up code might want to cache data in advance. Whether this is all the data derived from the underlying database tables (or any other source) or some part of the data is up to you to decide. You must balance the up-front startup cost of caching the data against the immediate cost of caching the data when the first request comes in that needs that particular data. If the server-side manager is prestarted as part of an AppServer session, which will be the case if the manager is made one of the required managers in the session type, then this startup cost might occur when the overall system is started, and the AppServer sessions might continue to run for a long time. In this case, an end user starting a client session or making a request from a client might never see the overhead of the server caching, making the precaching an appropriate action.
- If code that is executed on the client needs access to data that originates in database tables, the data must be passed from the server to the client so that the client does not need a database connection. If the data is likely to be needed again on the client, it makes sense to cache it in temp-tables there so that it’s available when needed.
- There might be a benefit on the client as well as the server to precaching data. Because the user will normally experience a wait when starting the application due to the overhead of precaching data, you must balance the benefit of having the data immediately available later on against the cost of loading it on startup.
- If data cached on either the client side or the server side might become stale or out-of-date, you must provide a way to determine when that data must be refreshed, and a mechanism to clear the old data from the client cache so that it is re-retrieved from the server, or to clear old data from the server cache so that it can be reloaded from the database.
- In a stateless environment, any server-side temp-table cache is local to a particular AppServer session, and if the data isn’t precached in a uniform way, the data in any AppServer session is simply a by-product of the client requests that happen to come in to that session. Since a client request can be handled by any available AppServer session, you can’t expect that data cached by some earlier request is in the cache of the server session that handles the next request. If server-side data is not precached, but it makes sense to cache data on the server at all, then you must simply allow for the fact that each AppServer session gradually builds up a cache reflecting the requests that it has handled. Thus, if precaching is not appropriate, you can still gain the efficiencies of a cache as the server sessions gradually build up data from the requests they handle.
In the case of the example Field Edit Manager, data caching makes sense only on the client. Each time an SDO starts up, it requests the field edit data for its enabled table or tables from the Field Edit Manager. If that data isn’t already available, then the client manager requests it from the server manager and adds it to the client cache.
The cache temp-table has the following definition:
The
Entity Namecomes from theEntity Mnemonictable, and theFieldname from theDisplay Fieldtable. TheEdit TypeandEdit Valuecome from the newField Edittable.The cacheFieldEdits procedure takes a list of one or more tables as input, and checks to see whether the field edit data for the tables) is already in the cache temp-table. Note that because the procedure will normally be called on the client, the section editor’s DB-Required toggle box must be checked off so that the code is compiled into the client version of the manager, as shown in Figure 7–5.
Figure 7–5: Section Editor
![]()
If any of the tables are not yet cached, then the code calls a separate procedure that has the
DB-Requiredflag set toTRUEto load them from the Repository database. ThecacheFieldEditsprocedure must check whether the procedure that reads the database is available to run locally or must be run remotely, so that it knows where to run the database-dependent code. Because theDB-Requiredprocedure is completely compiled out of the client manager when the manager is split between client and server, the code can check for the existence of theDB-Requiredprocedure in the manager’s internal entries. If it is not there, then it must be run remotely. This code fragment uses the include filedynlaunch.ito make the call, which is explained in the following example:
An alternative way to make this check is to use the following statement:
This has the disadvantage of hard-coding the logical database name where the data resides into the procedure, which might not be a good idea. If you later move the data (to your application database, for example) then you must remember to change this statement as well.
Using dynlaunch.i to make server calls in your manager
The include file dynlaunch.
isupports making a call to an internal procedure inside a server-side procedure, which might or might not already be running. It uses the Progress 4GL dynamicCallobject, which is new to Progress Version 9.1D, to package the parameters to the procedure call. It handles all these steps in a single AppServer call:
- Identifying whether the external procedure is already running on the server, and starting it as a persistent procedure if it isn’t.
- Running the internal procedure inside the server-side persistent procedure.
- Getting back the
OUTPUTparameters from the internal procedure call.- Deleting the server-side procedure if it was started just for this call.
As a result, using
dynlaunch.iis generally the preferred way to make calls to internal procedures across the AppServer connection in Progress Dynamics Version 2, as long as the external procedure on the server does need to be started and then left running after the call is complete.These are the basic include file arguments for
dynlaunch.i:
- The include file takes an
&PLIPnamed argument that is the name of the external procedure needed on the server. Alternatively, as in this example, it can be the logical name (theManagerTypename) of any registered Progress Dynamics manager, including a newly created one such as this.- The
&Iprocnamed argument is the name of the internal procedure to run.There must be three named arguments for each parameter in the internal procedure’s calling sequence. For each of these, the letter n represents the order of the parameters in the call:
As with all include file references, quoted strings must be inside single quotes. If a string argument includes spaces or other word breaks, then the single-quoted string must then be inside double quotes.
In the case of this example, the code is running the internal procedure
fetchFieldEditsin the server-sideFieldEditManager, and passing as anINPUTparameter a list of the tables to retrieve edit information for, and returning as anOUTPUTparameter a temp-table containing those edits.Writing the server-side caching procedure
The procedure that reads database data into the temp-table is separate from
cacheFieldEditsbecause it must have the Db-Required toggle box set toTRUE, so that it will be compiled only on the server-side, or when the database is otherwise available.This
fetchFieldEditsprocedure uses a copy of thettFieldEditDatatemp-table that must also be defined in the Definitions section of the manager, as shown below:
This is so that only the data for the current request is returned in the call. The client-side cache will gradually be built up as more requests are made. Because no data is maintained in memory on the server,
fetchFieldEditsfirst needs to empty out any leftover data from an earlier request. For example:
The procedure then goes through the list of requested tables, locates any
FieldEditrecords for their fields, and creates temp-table records for them, as shown:
Back in the calling procedure,
cacheFieldEdits, this data is copied into the client cache, as shown:
Note that it would be slightly more efficient to use the
APPENDkeyword with theOUTPUTparameter and have the new data added directly to the existing client cache. However, the underlying mechanism that supports dynlaunch.i does not allow this, so this is a penalty paid when using it. Keep in mind that in more complex caching examples, it is likely that there would be some overlap between data returned and data already on the client that couldn’t be checked in advance. For example, if you were to use the Repository Manager API to retrieve object definitions to cache on the client, then even when the parent object was uniquely identifiable, a single request could return a whole host of data for various object instances contained in that parent object, some of which might already be present in the client cache.In such a case you would have to receive new data in a separate table or set of tables anyway, and then do the necessary work on the client side to determine whether any of the data was already present, to avoid potential clashes of unique object IDs or other keys.
Clearing the client cache
In order to clear the cache on the client side if it must be refreshed, you can define a
clearClientCacheprocedure. This checks whether it is being invoked on the server or not, and empties the temp-table if it is on the client. The DB-Required toggle box must be checked off for this procedure, as shown:
In this way, any further requests of the cache on the client side will force a request to the server, since no cached data will be found on the client.
Note the use of the syntax, as shown:
This expression tells the code whether it is executing on the server-side of a connection that has a separate client. Only if this is not the case do you want to empty the cache temp-table, because it is not maintained on the server.
Note: In this example, you could safely empty the temp-table on the server as well, because nothing is maintained there anyway. However, in cases where data is cached separately on client and server, it could make a big difference which side you empty the cache on.The
NOTkeyword is included in the statement to make itTRUEfor the client side (including a stand-alone client with a local database connection) or omitted to make itTRUEfor the server side. TheSESSION:REMOTEattribute is true if this is an AppServer session. Otherwise, theCLIENT-TYPEattribute will be equal toWEBSPEEDif the session is a WebSpeed Agent. In either of these cases, the code is executing remotely relative to the user interface and presumes to have a database connection to the Repository data.Note that this expression is a little different in its effect from the earlier check of the
INTERNAL-ENTRIES, or for that matter making aCONNECTEDdatabase check. In the case where the client and server are effectively combined, that is, when the client session is running the full server-side manager with a database connection, none of theINTERNAL-ENTRIESorCONNECTEDchecks will returnTRUEbecause the full manager is running and the database is connected. But theNOT SESSION:REMOTEcheck will also returnTRUEbecause it is a not a remote session. This is as it should be, since a single session is doing the work of both client and server. Think about which of these types of checks you want to use in a particular situation, depending on the logic of the code being bracketed by the expression.Retrieving field edit data on the client
Now that you’ve taken care of getting field edit data cached on the client, you need a procedure to do lookups in it for client-side objects that need the information. You can do this with a procedure called
getFieldEditData. This takes input parameters for the table, field, and one or more types of edit types the caller needs values for, and returns a delimited list of the values. On the chance that the value for some newEdit Typemight itself contain a comma, theEdit ValuesOUTPUTparameter usesCHR(3)as a delimiter between the values for the requested edit types.The procedure simply looks up the requested records in the temp-table and returns their values, returning blank for edit types not defined for the fields in the temp-table. Note that the code presumes that the data is already available in the client cache, because the SDO for the table will have requested it. If this isn’t reliably the case (perhaps because
clearClientCachemight have been called after the SDO was initialized), this procedure could runcacheFieldEditsitself if the data needed isn’t available.Remember to check the DB-Required toggle box off for this procedure:
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |